Základné nastavenia¶
Importujeme potrebné knižnice. Nasledujúci odstavec by mal byť spustený ako prvý predtým ako sa budú používať importované objekty pd
, np
, plt
a sns
.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# nastavíme zobrazovanie grafov priamo v odstavcoch zápisníka
%matplotlib inline
# inicializujeme knižnicu seaborn
sns.set()
Dátová množina - Titanic¶
RMS Titanic bola britská osobná loď, ktorá stroskotala 15 apríla 1912 na svojej prvej komerčnej plavbe. V čase svojej služby bola najväčšou plávajúcou loďou na svete. Pri nehode zahynulo viac než 1500 osôb z celkového odhadovaného počtu 2224 pasažierov a členov posádky.
Dátová množina obsahuje informácie o pasažieroch Titanicu. Každý pasažier je popísaný nasledovnými atribútmi:
pclass
- trieda, v ktorej pasažier cestovalsurvived
- udáva, či pasažier nehodu parníku prežil (1), alebo nie (0)name
- meno cestujúcehosex
- pohlavieage
- veksibsp
- počet súrodencov pasažiera, resp. druhov/družiekparch
- počet rodičov/detí medzi cestujúcimiticket
- číslo lístkafare
- výška cestovnéhocabin
- kajuta v ktorej bol cestujúci ubytovanýembarked
- prístav, kde cestujúci nastúpil (S
- Southampton,C
- Cherbourg,Q
- Queenstown)
data = pd.read_csv("../data/titanic.csv") # načítame si dáta zo súboru ../data/titanic.csv
print(data.shape) # zobrazíme rozmer dátovej tabuľky v tvare (počet riadkov, počet stĺpcov)
print(data.columns) # zobrazíme názvy stĺpcov
data.head() # zobrazíme prvých 5 riadkov tabuľky
Úprava hodnôt¶
Keďže označenie lístka nevieme priamo interpretovať a nevieme z neho zistiť užitočné informácie o pasažieroch, stĺpec ticket
odstránime z dátovej tabuľky.
data.drop(columns="ticket", inplace=True) # odstránime stĺpec ticket priamo z dátového rámca data
V sĺpci embarked
si nahradíme skratky S
, Q
, a C
za Southampton
, Queenstown
a Cherbourg
.
# hodnoty kategorických atribútov môžete premapovať pomocou metódy map objektu typu Series
data["embarked"] = data["embarked"].map({"S": "Southampton", "Q": 'Queenstown', "C":"Cherbourg"})
data["embarked"].value_counts() # zobrazíme si rôzne hodnoty po nahradení a ich početnosti
Nahradenie chýbajúcich hodnôt 1¶
Jedným zo základných krokov predspracovania dát je spracovanie prázdnych hodnôt. Pri výskyte prázdnych hodnôt je potrebné rozlišovať, či ide o chýbajúce hodnoty - tzn. daný záznam by mal mať uvedenú hodnotu, ale z nejakého dôvodu nie je vyplnená, alebo či pre daný záznam daný atribút nemá význam uvádzať. Pri chýbajúcich hodnotách sa môžeme pokúsiť hodnoty doplniť tak, aby sme čo najmenej narušili vzťahy v dátach, resp. ak je chýbajúcich hodnôt príliš veľa, môžeme z dátovej množiny odstrániť jednotlivé stĺpce, alebo záznamy s chýbajúcimi hodnotami.
# metóda isna vráti tabuľku iba s Boolovskými hodnotami pre každý stĺpec (True - chýbajúca hodnota, False - neprázdna hodnota)
# sum potom spočíta počet hodnôt True (tzn. počet chýbajúcich hodnôt pre každý stĺpec)
data.isna().sum() # spočítame si počet chýbajúcich hodnôt
Nahradíme chýbajúe hodnoty pre stĺpec fare
.
p = data["fare"].hist() # vykreslíme si histogram hodnôt
# vypočítame si strednú hodnotu a medián
fare_mean = data["fare"].mean()
fare_median = data["fare"].median()
print("fare mean: {0:.4f}, median: {1:.4f}".format(fare_mean, fare_median))
# keďže hodnoty fare sú značne vychýlené, chýbajúce hodnoty nahradíme mediánom, ktorý lepšie charakterizuje najčastejšie sa vyskytujúce hodnoty
data["fare"].fillna(fare_median, inplace=True)
data["fare"].isna().sum() # skontrolujeme počet chýbajúcich hodnôt po nahradení
Úloha 8.1¶
Chýbajúce hodnoty pre atribút embarked
nahraďte najfrekventovanejšou hodnotou.
# nahraďte chýbajúce hodnoty
# skontrolujeme počet chýbajúcich hodnôt po nahradení
data["embarked"].isna().sum()
Odvodenie nových atribútov¶
Odvodením nových atribútov z existujúcich hodnôt môžeme z dátovej množiny často získať užitočné informácie, ktoré v nej nie sú priamo vyjadrené.
# vytvoríme si nový atribút family, ktorý bude udávať celkový počet príbuzných (súčet sibsp + parch)
data["family"] = data.eval("sibsp + parch")
p = data["family"].hist()
# vytvoríme si nový binárny atribút has_family, ktorý bude udávať, či pasažier cestoval s rodinou
data["has_family"] = data.eval("family > 0")
data["has_family"].sum() # spočítame, koľko pasažierov cestovalo s rodinou
Mená pasažierov sú vo formáte priezvisko, titul. meno
. Vyextrahujeme si z mena hodnoty titulov.
# definujeme si funkciu, ktorá z celého reťazca mena vyextrahuje iba časť titulu
def extract_title(name):
if pd.isna(name): # pomocou pd.isna otestujeme, či je name prázdna hodnota
return np.nan # ak je name prázdna hodnota, vrátime prázdnu hodnotu aj pre titul
# (prázdne hodnoty sú v pandas reprezentované číselnou konštantou np.nan - Not A Number)
start = name.find(",") + 1
end = name.find(".")
return name[start:end].strip() # z mena vrátime podreťazec od , do . (bez prázdnych znakov na začiatku a konci)
# pomocou metódy apply aplikujeme našu funkciu extract_title na všetky hodnoty stĺpca name a vrátené hodnoty uložíme v stĺpci title
data["title"] = data["name"].apply(extract_title)
# stĺpec name už nebudeme potrebovať, tak ho odstránime z dátovej tabuľky
data.drop(columns="name", inplace=True)
# zobrazíme si tituly a koľko krát sa vyskytli
data["title"].value_counts()
# pomocou metódy apply si premapujeme tituly na skrátený zoznam, ktorý priradíme do stĺpca title_short
def map_title(title):
# všetky hodnostné, alebo šľachtické tituly namapujeme na hodnotu 'rare title'
if title in {"Master", "Dr", "Rev", "Col", "Major", "Don", "Jonkheer", "Sir", "Dona", "Lady", "Capt", "the Countess"}:
return "rare title"
elif title in {"Mlle", "Ms"}: # tituly z francúzštiny
return "Miss"
elif title in {"Mme"}:
return "Mrs"
return title;
data["title_short"] = data["title"].apply(map_title)
data["title_short"].value_counts()
Nahradenie chýbajúcich hodnôt 2¶
Chýbajúce hodnoty jedného atribútu môžeme lepšie odhadnúť na základe iných atribútov. Napr. zobrazíme si rozdielny vek podľa pohlavia a titulu.
# v kontingenčnej tabuľke si zoskupíme dáta podľa pohlavia a titulu pasažierov a vypočítame základné štatistiky o veku
# parameter margins=True pridáva do kontingenčnej tabuľky celkové štatistiky pre každý riadok a stĺpec
pd.pivot_table(data, index=["sex", "title_short"], values="age", aggfunc=["median", "mean", "min", "max", "count"], margins=True)
# zaujímavý je nízky vek mužov s šľachtickým titulom, zobrazíme si histogram hodnôt v tejto skupine pasažierov
l = data.query("sex == 'male' and title_short == 'rare title'")["age"].hist()
Metódu apply
môžeme aplikovať aj na transformovanie/extrahovanie dát odvodených z hodnôt na danom riadku. V nasledujúcom kóde nahradíme chýbajúce hodnoty veku mediánom podľa hodnôt pohlavia a titulu.
# najprv si vypočítame medián pre jednotlivé skupiny pomocou kontingenčnej tabuľky
ptable = pd.pivot_table(data, index=["sex", "title_short"], values="age", aggfunc="median")
ptable
Riadky a stĺpce kontingenčnej tabuľky môžu byť označené na viacerých úrovniach, takže musíme pri prístupe k hodnotám tabuľky ako index zadať n-ticu hodnôt pre každú úroveň.
V našej tabuľke uloženej v premennej ptable
majú stĺpce len jednu úroveň (age
), ale riadky sú označené dvoma úrovňami (sex
a title_short
).
# ak chceme napr. vypísať konkrétnu hodnotu na riadku pre kombináciu (female, Miss), musíme zadať index ako n-ticu
# (podobne by sme museli zadaň n-ticu hodnôt aj pre stĺpce, ak by sme mali hierarchické označenia stĺpcov)
ptable["age"][("female", "Miss")]
# do premennej age1 si uložíme iba neprázdne hodnoty age (pôvodné dáta sa nezmenia)
age1 = data["age"].dropna() # metóda dropna vráti iba záznamy s neprázdnymi hodnotami
# definujeme si funkciu s ktorou nahradíme chýbajúce hodnoty veku podľa pohlavia a titulu
# row bude objekt reprezentujúci jeden riadok v tabuľke
def replace_missing_age(row):
# k hodnotám riadku pre jednotlivé stĺpce môžeme pristupovať indexovaním
age = row["age"] # zistíme si vek, pohlavie a titul pasažiera
sex = row["sex"]
title = row["title_short"]
# ak je vek chýbajúca hodnota, nahradíme ho mediánom v danej skupine určenej podľa pohlavia a titulu
# (vypočítané hodnoty mediánov máme uložené v kontingenčnej tabuľke ptable)
if pd.isna(age):
return ptable["age"][(sex, title)]
else:
return age # inak vrátime známu hodnotu
# aplikujeme funkciu replace_missing_age na každý riadok tabuľky (axis=1, prednastavená hodnota axis=0 by aplikovala funkciu po stĺpcoch)
# výsledok sú hodnoty age s nahradenými chýbajúcimi hodnotami, ktoré si uložíme do premennej age2
age2 = data.apply(replace_missing_age, axis=1)
# pre porovnanie si naraz vykreslíme histogram bez nahradenia a po nahradení chýbajúcich hodnôt
p = plt.hist([age1, age2])
data["age"] = age2 # nahradíme stĺpec age s vyplnenými chýbajúcimi hodnotami v pôvodných dátach
Prevedenie číselných atribútov na ordinálne - diskretizácia hodnôt¶
Číselné atribúty je možné jednoducho previesť na ordinálne (usporiadané kategorické) atribúty rozdelením na intervaly. Hodnoty môžu byť rozdelené na rovnako široké intervaly, alebo podľa dát na rôzne intervaly tak aby bola početnosť hodnôt v každom intervale približne rovnaká.
data["fare"].describe() #zobrazíme si základné štatistiky pre atribút fare
data["fare_ordinal"] = pd.cut(data["fare"], 3) # pomocou metódy cut rozdelíme hodnoty do 3 rovnako veľkých intervalov
data["fare_ordinal"].value_counts() # zobrazíme si označenia intervalov a ich početnosti
# väčšina hodnôt patrí približne do intervalu od 0-170, pre lepšie pochopenie distribúcie hodnôt si zobrazíme histogram
p = data["fare"].hist(bins=20) # parameter bins udáva počet intervalov pre výpočet histogramu
# namiesto rozdelenia na rovnako veľké intervaly môžeme dáta rozdeliť pomocou metódy qcut na intervaly s približne rovnakým počtom hodnôt
data["fare_ordinal"] = pd.qcut(data["fare"], 3)
data["fare_ordinal"].value_counts() # zobrazíme si označenia intervalov a ich početnosti
# pri metóde cut môžeme priamo zadať hraničné hodnoty intervalov, napr. rozdelíme fare na intervaly [0-25], (25-100] a (100, 520]
# štandardne prvý interval nezahŕňa najmenšiu hodnotu, takže ak chceme zahrnúť aj 0 hodnoty, nastavíme include_lowest na True
data["fare_ordinal"] = pd.cut(data["fare"], bins=[0, 25, 100, 520], include_lowest=True)
data["fare_ordinal"].value_counts()
# označenia intervalov môžeme priamo pomenovať zadaním parametra labels
data["fare_ordinal"] = pd.cut(data["fare"], bins=[0, 25, 100, 520], include_lowest=True, labels=["normal", "more expensive", "most expensive"])
data["fare_ordinal"].value_counts()
Závislosti medzi kategorickými atribútmi¶
Závislosti medzi kategorickými atribútmi môžete skúmať pomocou krížových tabuliek, ktoré udávajú početnosť výskytu všetkých kombinácií hodnôt medzi zvolenými kategorickými atribútmi podobne, ako kontingenčné tabuľky sumarizujú závislosť medzi kategorickými a číselnými hodnotami. Krížové tabuľky sa generujú pomocou metódy crosstab
.
# vypočítame tabuľku početností pre všetky kombinácie hodnôt medzi atribútmi pclass a sex
# všimnite si, že do metódy crosstab musíte narozdiel od pivot_table zadať ako index a columns
# priamo dátové atribúty (objekty typu Series)
pd.crosstab(index=data["pclass"], columns=data["sex"])
# podobne ako pri kontingenčnej tabuľke, ako riadky, alebo stĺpce môžeme zadať viacero
# atribútov
# napr. v nasledujúcej tabuľke vypočítame pre každú kombináciu triedy a výšky cestovného,
# koľko žien a koľko mužov si kúpilo daný lístok
pd.crosstab(index=[data["pclass"], data["fare_ordinal"]], columns=data["sex"])
Úloha 8.2¶
Odvoďte nový atribút age_ordinal
diskretizovaním hodnôt age
na intervaly 0-13, 13-19, 19-65, 65-maximálny vek s označeniami child
, young
, adult
, old
.
Úloha 8.3¶
Pomocou krížovej tabuľky zistite, koľko mužov a žien má aký titul.
Úloha 8.4¶
Vytvorte kontingenčnú tabuľku v ktorej prehľadne zobrazíte počet zachránených pasažierov pre skupiny rozdelené podľa veku (age_ordinal
), triedy a pohlavia. Ak použijete ako agregačnú funkciu strednú hodnotu survived
, ako môžete výsledné čísla interpretovať?
Úloha 8.5¶
Zistite, či má na prežitie vplyv výška cestovného, alebo miesto nalodenia.
Úloha 8.6 - bonusová úloha¶
Označenia kajuty začínajú písmenom, ktoré označuje palubu na ktorej sa kajuta nachádzala (napr. kajuta C22
sa nachádzala na palube C
, atď.). Pomocou metódy apply
odvoďte nový atribút deck
s označením paluby.
Na ktorej palube by ste mali väčšiu šancu na záchranu?